using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace Comteck.Winforms.Tasks {
  /// <summary>
  /// 任务管理器
  /// </summary>
  public class TaskManager : BaseLog, IDisposable {
    private Task runningTask;
    private CancellationTokenSource runningToken;
    private readonly ConcurrentQueue<Task> taskQueue;
    private readonly SemaphoreSlim workEvent = new SemaphoreSlim(0);
    /// <summary>
    /// 任务管理器名称
    /// </summary>
    public virtual string Name { get; set; } = nameof(TaskManager);
    public event EventHandler<AsynEventArgs<Exception>> ProcessException;
    /// <summary>
    /// 是否启用日志
    /// </summary>
    public virtual bool LogEnabled { get; set; } = true;
    public EnumTaskManagerType TaskManagerType { get; }
    public delegate void OnTaskRunningErrorEventHandle(Exception ex);
    public OnTaskRunningErrorEventHandle OnTaskRunningErrorEvent;
    public TaskManager() : this(EnumTaskManagerType.Normal) {

    }

    public TaskManager(EnumTaskManagerType type) {
      this.TaskManagerType = type;
      workEvent = type == EnumTaskManagerType.Only ? new SemaphoreSlim(0, 1) : new SemaphoreSlim(0);
      taskQueue = new ConcurrentQueue<Task>();
      this.Start();
    }

    public int QueueCount => taskQueue.Count;
    public bool IsRunning => !(runningToken?.IsCancellationRequested ?? true);
    /// <summary>
    /// 开启执行
    /// </summary>
    public void Start() {
      if (this.IsRunning)
        return;
      runningToken = new CancellationTokenSource();
      var context = SynchronizationContext.Current;
      if (context != null) {
        SynchronizationContext.SetSynchronizationContext(null);
      }
      try {
        if (runningTask != null && !runningTask.IsCompleted) {
          runningTask = this.WaitBeforeAndRun(runningTask, runningToken.Token);
        } else {
          runningTask = this.ProcessTask(runningToken.Token);
        }
        Logger.Info($"{this.Name} - 队列开始运行:等待接收");
      } finally {
        if (context != null) {
          SynchronizationContext.SetSynchronizationContext(context);
        }
      }
    }

    private async Task WaitBeforeAndRun(Task beforeTask, CancellationToken cancellationToken) {
      try {
        await beforeTask;
      } catch { }
      await ProcessTask(runningToken.Token);
    }

    /// <summary>
    /// 
    /// </summary>
    public void Stop() {
      Logger.Info($"{this.Name} - 队列关闭开始:对象释放, 剩余任务数 -> {this.QueueCount}");
      runningToken.Cancel();
      //try {
      //  process_Task.Wait();
      //} catch {
      //}
      Logger.Info($"{this.Name} - 队列关闭结束:对象释放");
    }

    /// <summary>
    /// 加入队列
    /// </summary>
    /// <param name="item"></param>
    public void Enqueue(Task item) {
      if (item == null) {
        throw new ArgumentException("items");
      }

      if (this.LogEnabled) {
        Logger.Info($"{this.Name} - 加入队列开始:{item.Id}");
      }

      if (this.TaskManagerType == EnumTaskManagerType.Only) {
        lock (this) {
          while (taskQueue.TryDequeue(out _)) { }

          taskQueue.Enqueue(item);
          workEvent.Release();
        }
      } else {
        taskQueue.Enqueue(item);
        workEvent.Release();
      }

      if (this.LogEnabled) {
        Logger.Info($"{this.Name} - 加入队列结束:{item.Id}");
      }
    }

    /// <summary>
    /// 取出队列
    /// </summary>
    /// <returns></returns>
    public Task Dequeue() {
      if (this.LogEnabled) {
        Logger.Info($"{this.Name} - 队列取出开始！");
      }

      Task tmpTask = null;

      if (this.TaskManagerType == EnumTaskManagerType.Last) {
        while (taskQueue.TryDequeue(out var lastTask)) {
          tmpTask = lastTask;
        }
      } else if (this.TaskManagerType == EnumTaskManagerType.Normal) {
        if (!taskQueue.TryDequeue(out tmpTask)) {
          Logger.Info($"{this.Name} - 队列取出错误！");
          return null;
        }
      } else {
        lock (this) {
          if (!taskQueue.TryDequeue(out tmpTask)) {
            Logger.Info($"{this.Name} - 队列取出错误！");
            return null;
          }
        }
      }
      if (this.LogEnabled) {
        Logger.Info($"{this.Name} - 队列取出结束！");
      }

      return tmpTask;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="task"></param>
    private async Task RunTask(Task task) {

      try {
        if (this.LogEnabled) {
          Logger.Info($"{this.Name} - 运行任务开始:{task.Id}");
        }
        task.Start();
        await task;

        if (this.LogEnabled) {
          Logger.Info($"{this.Name} - 运行任务结束:{task.Id}");
        }
      } catch (Exception ex) {
        Logger.Error($"{this.Name} - 运行任务异常:{task.Id}", ex);
        OnTaskRunningErrorEvent?.Invoke(ex);
      }
    }

    /// <summary>
    ///定时处理线程调用函数  
    ///主要是监视入队的时候线程 没有来的及处理的情况
    /// </summary>
    private async Task ProcessTask(CancellationToken cancellationToken) {
      //判断是否需要执行
      while (!cancellationToken.IsCancellationRequested) {
        await workEvent.WaitAsync(cancellationToken);
        try {
          var tmpTask = this.Dequeue();

          if (tmpTask != null) {
            Logger.Info($"{this.Name} - 处理队列开始:{tmpTask.Id}");
            await this.RunTask(tmpTask);
            Logger.Info($"{this.Name} - 处理队列结束:{tmpTask.Id}");
          }
        } catch (Exception ex) {
          Logger.Error($"{this.Name} - 队列执行异常", ex);
        }
      }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="ex"></param>
    private void OnProcessException(Exception ex) {
      var tempException = ProcessException;
      Interlocked.CompareExchange(ref ProcessException, null, null);

      if (tempException != null) {
        ProcessException(ex, new AsynEventArgs<Exception>(ex));
      }
    }

    #region IDisposable 成员

    /// <summary>
    /// 
    /// </summary>
    public void Dispose() {
      this.Stop();
    }

    #endregion
  }
}
